<?php
/**
 * 基础库(HTTP操作通用处理函数)
 */

namespace Deyi\Functions;

use Apps\Common\Constant\CacheTimeoutConstant;
use Dns2\Dns2;
use Phalcon\Di;

/**
 * Class HttpFunction
 * @package Deyi\Functions
 */
class HttpFunction
{
    // 请求重试次数设定
    const RETRIES = 3;

    /**
     *  请求指定URL页面并获取返回的数据
     *
     * @author liang<lang@vip.deyi.com>
     * @param $url
     * @param int $timeout
     * @param bool $useDnsCache
     * @param array $headers
     * @param string $manualIp
     * @param string $referer
     * @param boolean $useGzip
     * @param array $cookies
     * @return mixed
     */
    public static function getPage($url, $timeout = 2, $useDnsCache = false, $headers = [], $manualIp = '', $referer = '', $useGzip = false, $cookies = [])
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36');

        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

        if ($referer) {
            curl_setopt($ch, CURLOPT_REFERER, $referer);
        }

        if ($useGzip) {
            curl_setopt($ch, CURLOPT_ENCODING, "gzip");
        }

        if ($cookies) {
            $cookieTmp = [];
            foreach ($cookies as $k => $v) {
                $cookieTmp[] = $k . '=' . rawurlencode($v);
            }

            curl_setopt($ch, CURLOPT_COOKIE, implode(';', $cookieTmp));
            unset($cookieTmp);
        }

        $isUseDnsCache = false;
        $domainName = parse_url($url, PHP_URL_HOST);
        if (($useDnsCache && $domainName && !ip2long($domainName)) || $manualIp) {
            $host = $manualIp ? $manualIp : self::getOneHostByUrl($url);
            if ($host) {
                $url = str_replace($domainName, $host, $url);
                $isUseDnsCache = true;
            }
        }

        if ($isUseDnsCache) {
            $headers[] = 'Host: ' . $domainName;
        }

        if (!empty($headers)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }

        curl_setopt($ch, CURLOPT_URL, $url);

        $result = false;
        $retries = self::RETRIES;
        $errorCode = 0;
        while (($result === false) && (--$retries >= 0)) {
            $result = curl_exec($ch);
            if ($errorCode = curl_errno($ch)) {
                usleep(rand(200000, 600000)); // 随机休息一下重试
            }
        }
        if ($errorCode) {
            Di::getDefault()->get('logger')->error('curl fail:' . $url . ', code:' . $errorCode);
        }
        curl_close($ch);

        return $result;
    }

    /**
     * Post数据到指定URL
     * @version APP6.3.0
     * @author lang@vip.deyi.com
     * @param $url
     * @param $postData
     * @param int $timeout
     * @param array $ssl
     * @param array $headers
     * @param array $cookies
     * @return mixed
     */
    public static function postDataToUrl($url, $postData, $timeout = 5, $ssl = [], $headers = [], $cookies = [])
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36');
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, $timeout);

        if (!empty($headers)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }

        if ($cookies) {
            $cookieTmp = [];
            foreach ($cookies as $k => $v) {
                $cookieTmp[] = $k . '=' . rawurlencode($v);
            }

            curl_setopt($ch, CURLOPT_COOKIE, implode(';', $cookieTmp));
            unset($cookieTmp);
        }

        if (!empty($ssl)) {
            foreach ($ssl as $k => $v) {
                curl_setopt($ch, $k, $v);
            }
        }

        $result = false;
        $retries = self::RETRIES;
        $errorCode = 0;
        while (($result === false) && (--$retries >= 0)) {
            $result = curl_exec($ch);
            if ($errorCode = curl_errno($ch)) {
                usleep(rand(200000, 600000)); // 随机休息一下重试
            }
        }
        if ($errorCode) {
            Di::getDefault()->get('logger')->error('curl fail:' . $url . ', code:' . $errorCode);
        }
        curl_close($ch);

        return $result;
    }

    /**
     *  对指定URL发起删除请求
     *
     * @author liang<lang@vip.deyi.com>
     * @param $url
     * @param int $timeout
     * @return mixed
     */
    public static function deleteToUrl($url, $timeout = 2)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36');
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, $timeout);

        $result = false;
        $retries = self::RETRIES;
        $errorCode = 0;
        while (($result === false) && (--$retries >= 0)) {
            $result = curl_exec($ch);
            if ($errorCode = curl_errno($ch)) {
                usleep(rand(200000, 600000)); // 随机休息一下重试
            }
        }
        if ($errorCode) {
            Di::getDefault()->get('logger')->error('curl fail:' . $url . ', code:' . $errorCode);
        }
        curl_close($ch);

        return $result;
    }

    /**
     * PUT 数据到指定url
     *
     * @author liang<lang@vip.deyi.com>
     * @param $url
     * @param $postData
     * @param int $timeout
     * @return mixed
     */
    public static function putToUrl($url, $postData, $timeout = 2)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36');
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
        curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, $timeout);

        $result = false;
        $retries = self::RETRIES;
        $errorCode = 0;
        while (($result === false) && (--$retries >= 0)) {
            $result = curl_exec($ch);
            if ($errorCode = curl_errno($ch)) {
                usleep(rand(200000, 600000)); // 随机休息一下重试
            }
        }
        if ($errorCode) {
            Di::getDefault()->get('logger')->error('curl fail:' . $url . ', code:' . $errorCode);
        }
        curl_close($ch);

        return $result;
    }

    /**
     *  获取指定url的host
     *
     * @author liang<lang@vip.deyi.com>
     * @param $url
     * @return null
     */
    public static function getOneHostByUrl($url)
    {
        $urlHost = parse_url($url, PHP_URL_HOST);

        if (!$urlHost) {
            return null;
        }

        $cacheKey = 'dns_cache_' . $urlHost;
        $hosts = apcu_fetch($cacheKey);

        if (!$hosts) {
            try {
                $dnsResult = Dns2::getInstance([
                    'nameservers' => ['114.114.114.114', '119.29.29.29'],
                    'ns_random' => true,
                    'cache_type' => 'file'
                ])->query($urlHost);

                if (isset($dnsResult->answer) && count($dnsResult->answer) >= 1) {
                    $hosts = [];
                    foreach ($dnsResult->answer as $v) {
                        if (isset($v->address)) {
                            $hosts[] = $v->address;
                        }
                    }

                    $hosts ? apcu_store($cacheKey, $hosts, 120) : '';
                }

            } catch (\Exception $e) {

            }

        }

        if (!empty($hosts)) {
            shuffle($hosts);

            return array_pop($hosts);
        }

        return null;
    }

    /**
     * 数据下载功能方法
     * @author lang@vip.deyi.com
     * @param $url
     * @param $toPath
     * @param $refer
     * @return bool
     */
    public static function downFileFromUrl($url, $toPath, $refer = '')
    {
        $res = static::getPage($url, 60, true, [], '', $refer);

        if ($res && @file_put_contents($toPath, $res)) {
            return true;
        }

        return false;
    }

    /**
     * 将临时文件放入缓存目录
     * @author lang@vip.deyi.com
     * @param $url
     * @param $referer
     * @return bool|string
     */
    public static function downTempFileToCacheFolder($url, $referer = '')
    {
        $tmpFile = ROOT_PATH . '/data/cache/' . uniqid('tmp');

        if (!$referer) {
            $pUrl = parse_url($url);
            $referer = $pUrl['scheme'] . '://' . $pUrl['host'];
        }

        if (!static::downFileFromUrl($url, $tmpFile, $referer)) {
            return false;
        }
        //识别文件类型,如果是图片文件就在末尾加一个文件后缀
        $extName = UtilFunction::getImageTypeInfo($tmpFile);
        //计算文件md5值
        $fileNameMd5 = md5_file($tmpFile);
        $newFileName = $fileNameMd5 . ($extName ? ('.' . $extName) : '');
        $newFilePath = ROOT_PATH . '/data/cache/' . $newFileName;
        rename($tmpFile, $newFilePath);

        return $newFileName;
    }

    /**
     * 清理临时文件目录
     * @author lang@vip.deyi.com
     * @param int $expired
     */
    public static function cleanCacheFolderExpiredFiles($expired = CacheTimeoutConstant::LOCAL_DAY_CACHE)
    {
        $cleanDir = ROOT_PATH . '/data/cache/';
        $dir = dir($cleanDir);
        while (($item = $dir->read()) !== false) {
            if ($item == '.' || $item == '..') {
                continue;
            } else {
                $file = $cleanDir . $item;
                $times = time() - filemtime($file);
                if ($times > $expired) {
                    @unlink($file);
                }
            }
        }
    }

    /**
     * 通过CURL上传文件到指定url
     * @author lang@vip.deyi.com
     * @param $file
     * @param $formName
     * @param $url
     * @param $timeout
     * @return mixed
     */
    public static function uploadFileToUrl($file, $formName, $url, $timeout = 5)
    {
        $ch = curl_init();
        $postData = [];
        if (function_exists('curl_file_create')) {// 检测判断php版本
            $postData = ['file' => curl_file_create(realpath($file), UtilFunction::getFileMimeType($file))];
        } else {
            if (defined('CURLOPT_SAFE_UPLOAD')) {
                curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
            }
            $postData = ['name' => $formName, 'file' => '@' . realpath($file)];//<=5.5
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, $timeout);

        $result = curl_exec($ch);

        if ($errorCode = curl_errno($ch)) {
            Di::getDefault()->get('logger')->error('curl upload file fail:' . $url . ', code:' . $errorCode);
        }
        curl_close($ch);

        return $result;
    }
}
